package com.atwisdom.star.log.aop;

import cn.hutool.core.lang.Console;
import com.alibaba.fastjson.JSONObject;
import com.atwisdom.star.log.mapper.SysLogMapper;
import com.atwisdom.star.log.pojo.SysLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
@Aspect
public class StarLog {
    private final SysLogMapper sysLogMapper;
    private Object[] args;
    @Autowired
    HttpServletRequest request;
    public StarLog(SysLogMapper sysLogMapper) {
        this.sysLogMapper = sysLogMapper;
    }
    @Pointcut("@annotation(com.atwisdom.star.log.aop.StarAnnotation)")//用于匹配带有注解的方法
//    @Pointcut(value = "execution(* com.atwisdom.star.*.controller..*.*(..))")
    private void pointcut(){
    }
    @Before("pointcut()")
    public void myBefore(JoinPoint joinpoint){
        args = joinpoint.getArgs();
        SysLog log=new SysLog(joinpoint.getSignature().toLongString(),null);
        this.sysLogMapper.insert(log);
    }
    @AfterReturning(returning = "object", pointcut = "pointcut()")
    public void afterReturning(Object object) {
        if(null==request.getHeader("debug")||"development".equals(request.getHeader("debug"))){
            Console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
            Console.log("environment:"+request.getHeader("debug"));
            Console.log("request:"+request.getRequestURI());
            Console.log("params:"+ args[0]);
            Console.log("return:"+JSONObject.parseObject(JSONObject.toJSONString(object)));
            Console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        }
    }
}
//public class AutoFillAspect {/*** 定义切点,表示具体执行的点，也就是什么时候执行通知的点**/
//    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
//    public void autoFillPointCut(){}/*** 定义前置通知,在当前方法中进行公共字段的填充赋值* @param joinPoint 就是spring中允许使用通知的地方*/
//    @Before("autoFillPointCut()")
//    public void autoFill(JoinPoint joinPoint){
//        log.info("开始进行公共字段填充");
//    }
//1.获取到当前被拦截方法上的数据操作类型
// 1.1获取方法签名MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 1.2获取方法上的自定义注解对象AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
// 1.3 获取自定义注解中的操作类型，insert 还是updateOperationType operationType = autoFill.value();
// 2.获取当前被拦截方法的参数---实体对象
// 2.1 获取参数Object[] args = joinPoint.getArgs();if(args==null || args.length==0){return;}
// 2.2 获取第一个参数，可能是实体类对象也有可能是其他对象Object entity = args[0];
// 3.3 定义属性赋值参数LocalDateTime now = LocalDateTime.now();Long userId = UserThreadLocalUtils.getCurrentId();
// 3.如果是新增，则为四个属性赋值if(operationType==OperationType.INSERT){try {
// 3.1 利用反射获取当前赋值的方法,也就是实体类中的set赋值方法
// 创建时间Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
// 创建人Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
// 修改时间Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
// 修改人Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
// /***
// 3.2 为获取的以上四个set方法进行赋值*  invoke(Object obj, Object... args)*/
// setCreateTime.invoke(entity,now);
// setCreateUser.invoke(entity,userId);
// setUpdateTime.invoke(entity,now);
// setUpdateUser.invoke(entity,userId);} catch (Exception e) {e.printStackTrace();}
// 4.如果是修改，则为两个属性赋值}else if(operationType==OperationType.UPDATE){try {
// 修改时间Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
// 修改人Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
// 通过反射为对象属性赋值setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,userId);} catch (Exception e) {e.printStackTrace();}}}
/*
 使用时生效需要bean也是通过代理完成的
 如果不是，则在调用中使用
 @Autowired
 private ApplicationContext applicationContext;
 通过代理调用方法：
 applicationContext.getBean(类名.class).method(xxx)
 则该方法上的注解生效

 */
/*
 2.1 JointPoint
 JointPoint是程序运行过程中可识别的点，这个点可以用来作为AOP切入点。JointPoint对象则包含了和切入相关的很多信息。比如切入点的对象，方法，属性等。我们可以通过反射的方式获取这些点的状态和信息，用于追踪tracing和记录logging应用信息。

 2.2 @Pointcut
 通过@Pointcut定义切入点，可作用于全部方法或指定方法
 execution：用于匹配方法执行的连接点
 @annotation：用于匹配当前执行方法持有指定注解的方法

 //作用于service中所有方法
 @Pointcut("execution(* com.demo.service.*(..))")

 //作用与所有接口
 @Pointcut("execution( public * com.demo.controller.*Controller.*(..))")

 //作用与加了注解的指定接口，注解形式配置参考第1部分
 @Pointcut("@annotation( com.demo.utils.AutoLog)")

@Before 定义了一个前置通知，是在所拦截方法执行之前执行一段逻辑。
@After 是在所拦截方法执行之后执行一段逻辑。
@Around 是可以同时在所拦截方法的前后执行一段逻辑。
@AfterReturning 是可以获取该执行方法的返回值。
@AfterThrowing 定义了一个异常通知，若方法抛出了Exception类型的异常，都会回调afterThrowing方法。
 */
/*
1.获取到当前被拦截方法上的数据操作类型
 1.1获取方法签名MethodSignature signature = (MethodSignature) joinPoint.getSignature();
 1.2获取方法上的自定义注解对象AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
 1.3 获取自定义注解中的操作类型，insert 还是updateOperationType operationType = autoFill.value();
 2.获取当前被拦截方法的参数---实体对象
 2.1 获取参数Object[] args = joinPoint.getArgs();if(args==null || args.length==0){return;}
 2.2 获取第一个参数，可能是实体类对象也有可能是其他对象Object entity = args[0];
 3.3 定义属性赋值参数LocalDateTime now = LocalDateTime.now();Long userId = UserThreadLocalUtils.getCurrentId();
 3.如果是新增，则为四个属性赋值if(operationType==OperationType.INSERT){try {
 3.1 利用反射获取当前赋值的方法,也就是实体类中的set赋值方法
 创建时间Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
 创建人Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
 修改时间Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
 修改人Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
 /***
 3.2 为获取的以上四个set方法进行赋值*  invoke(Object obj, Object... args)
 setCreateTime.invoke(entity,now);
 setCreateUser.invoke(entity,userId);
 setUpdateTime.invoke(entity,now);
 setUpdateUser.invoke(entity,userId);} catch (Exception e) {e.printStackTrace();}
 4.如果是修改，则为两个属性赋值}else if(operationType==OperationType.UPDATE){try {
 修改时间Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
 修改人Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
 通过反射为对象属性赋值setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,userId);} catch (Exception e) {e.printStackTrace();}}}
*/
